﻿package game.alternativa {
	import flash.system.System;
	import flash.display.BitmapData;
	import game.objects.Game3DObject;
	import game.debug.Debug;
	import game.fileloader.FileLoader;
	import game.fileloader.FileLoaderEvent;
	import game.objects.TextureObject;
	import alternativa.engine3d.loaders.ParserA3D;
	import alternativa.engine3d.lights.AmbientLight;
	import alternativa.engine3d.resources.ExternalTextureResource;
	import alternativa.engine3d.core.Resource;
	import alternativa.engine3d.loaders.TexturesLoader;
	import alternativa.engine3d.loaders.events.TexturesLoaderEvent;
	import alternativa.engine3d.loaders.ParserMaterial;
	import alternativa.engine3d.core.Object3D;
	import alternativa.engine3d.objects.Mesh;
	import alternativa.engine3d.resources.TextureResource;
	import alternativa.engine3d.core.BoundBox;
	
	public class Model extends Game3DObject {
		
		private var parser:ParserA3D;
		private var loader:FileLoader;
		private var util:UtilTextures;
		private var model:String;		//ссылка на модель
		private var materials:Object;	//ссылки ExternalTextureResource
		private var textureObjects:Vector.<TextureObject>;
		private var contextInit:Boolean		= false;
		private var a3dInit:Boolean			= false;
		private var texturesInit:Boolean	= false;
		private var _loading:Boolean		= false;
		private var _alpha:Number			= 1;
		
		public function Model():void {
			loader		= new FileLoader;
			materials	= new Object;
			
			loader.addEventListener( FileLoaderEvent.LOADED,	loadedHandler );
			loader.addEventListener( FileLoaderEvent.ERROR,		errorHandler );
		}
		
		public function load():void {
			//начало загрузки, после получения Context3D
			contextInit = true;
			
			if (model && !_loading)
				loadA3D();
		}
		
		public function loadA3D():void {
			//загрузка a3d
			if (!contextInit || !model || _loading) return;
			
			a3dInit = false;
			_loading = true;
			parser = null;
			util = null;
			
			loader.set(model);
		}
		
		public function loadTextures():void {
			//загрузка текстур
			if (!contextInit || !a3dInit || !materials || _loading) return;
			
			texturesInit = false;
			
			_loading = true;
			
			if (vectorMaterials.length>0) {
				var textureLoader:TexturesLoader = new TexturesLoader(context3D);
				textureLoader.addEventListener(TexturesLoaderEvent.COMPLETE, textureComplete);
				textureLoader.loadResources(vectorMaterials);
			} else {
				textureComplete(null);
			}
		}
		
		public function getTexture(type:String):TextureResource {
			return materials[type] as TextureResource;
		}
		
		public function set a3d (value:String):void {
			//устанавливаем объект a3d
			model = value;
			
			if (contextInit)
				loadA3D();
		}
		
		public function set textures (value:Vector.<TextureObject>):void {
			textureObjects = value;
			
			//устанавливаем объект materials
			for each (var res:TextureObject in value) 
				materials[res.type] = UtilTextures.getResource(res);
			
			if (contextInit && a3dInit)
				loadTextures();
		}
		
		public function set mat(value:Object):void {
			materials = value;
		}
		
		private function textureComplete(event:TexturesLoaderEvent) {
			_loading = false;
			
			if (event) {
				for each (var textureResource:ExternalTextureResource in event.getTextures()) {
					if (!textureResource.isUploaded) {
						//если текстура не загружена очищаем ее из модели
						for (var i in materials) {
							if (materials[i] == textureResource.url)
								materials[i] = null;
						}
						
						this.dispatchEvent( new ModelEvent ( ModelEvent.TEXTURE_ERROR, {url: textureResource.url} ) );
						debug('Error Texture '+textureResource.url, Debug.MODELS);
					}
				}
			}
			
			util.setupMaterials(materials, _alpha);
			
			this.dispatchEvent( new ModelEvent ( ModelEvent.TEXTURE_COMPLETE ) );
			
			texturesInit = true;
			
			//System.gc();
		}
		
		public function cloneModel():Model {
			var m:Model = new Model;
			if (model)
				m.a3d = model;
			if (textureObjects)
				m.textures = textureObjects;
			m.alpha = _alpha;
			m.x = this.x;
			m.y = this.y;
			m.z = this.z;
			m.scaleX = this.scaleX;
			m.scaleY = this.scaleY;
			m.scaleZ = this.scaleZ;
			m.rotationX = this.rotationX;
			m.rotationY = this.rotationY;
			m.rotationZ = this.rotationZ;
			return m;
		}
		
		public function set alpha(value:Number):void {
			_alpha = value;
		}
		
		private function errorHandler( event:FileLoaderEvent ):void {
			_loading = false;
			
			dispatchEvent( new ModelEvent ( ModelEvent.MODEL_ERROR ) );
			debug('Error Model: '+model, Debug.MODELS);
		}
		
		public function loadedHandler ( event:FileLoaderEvent ):void { 
			_loading = false;
			
			
			while (numChildren)
				removeChildAt(0);
			
			parser = new ParserA3D();
			parser.parse(event.content);
			
			var objects:Vector.<Object3D> = hierarchy;
			for each(var object:Object3D in objects) {
				addChild(object);
			}
			
			util = new UtilTextures(context3D, parser.objects);
			
			util.setupMaterials(null);
			
			this.dispatchEvent( new ModelEvent ( ModelEvent.MODEL_COMPLETE ) );
			
			a3dInit = true;
			loadTextures();
		}
		
		//возвращает материалы которые нужно загрузить
		public function get vectorMaterials():Vector.<ExternalTextureResource> {
			var v:Vector.<ExternalTextureResource> = new Vector.<ExternalTextureResource>;
			for each (var res:* in materials) {
				if (res is ExternalTextureResource) {
					//if (!(res as ExternalTextureResource).isUploaded)
						v.push(res);
				}
			}
			return v;
		}
		
		public function update():void {
			util.context3D = context3D;
			util.setupMaterials(null, _alpha);
			
		}
		
		public function get hierarchy():Vector.<Object3D> {
			var objects:Vector.<Object3D> = new Vector.<Object3D>;
			if (parser) {
				for each (var object in parser.hierarchy) {
					if (object is AmbientLight) continue;
					if (object is Mesh || object is Object3D) {
						objects.push ( object );
					}
				}
			}
			return objects;
		}
		
		public function get size():BoundBox {
			return (hierarchy.length>0) ?	(
												(hierarchy[0].numChildren == 0 && hierarchy[0] is Mesh) ?
												(hierarchy[0] as Mesh).boundBox : 
												(
													(hierarchy[0].getChildAt(0) is Mesh) ?
													(hierarchy[0].getChildAt(0) as Mesh).boundBox : null
												)
											) : null;
		}
		
	}
	
}